import org.junit.Before;
import org.junit.Test;

import java.util.Iterator;

import static junit.framework.Assert.*;

/**
 * Created by IntelliJ IDEA.
 * User: Roman
 * Date: 11.09.12
 * Time: 21:32
 * To change this template use File | Settings | File Templates.
 */
public class BoardTest {
    /*
     * hamming = 0 manhattan = 0
     */
    private final int[][] valuesGoal = {{1, 2, 3}, {4, 5, 6}, {7, 8, 0}};
    /*
     * hamming = 7 manhattan = 14
     */
    private int[][] valuesBoardA = {{4, 5, 6}, {3, 0, 1}, {2, 8, 7}};
    // Neighbors of A
    private int[][] valuesBoardALeftNeighbour = {{4, 5, 6}, {0, 3, 1}, {2, 8, 7}};
    private int[][] valuesBoardARightNeighbour = {{4, 5, 6}, {3, 1, 0}, {2, 8, 7}};
    private int[][] valuesBoardAUpNeighbour = {{4, 0, 6}, {3, 5, 1}, {2, 8, 7}};
    private int[][] valuesBoardADownNeighbour = {{4, 5, 6}, {3, 8, 1}, {2, 0, 7}};

    /*
     * hamming = 1 manhattan = 1
     */
    private int[][] valuesBoardB = {{1, 2, 3}, {4, 5, 6}, {7, 0, 8}};

    private Board A;
    private Board B;
    private Board goal;

    @Before
    public void setUp() throws Exception {
        goal = new Board(valuesGoal);
        A = new Board(valuesBoardA);
        B = new Board(valuesBoardB);
    }

    @Test
    public void testDimension() throws Exception {
        assertEquals("Something wrong with dimensions",valuesGoal.length,goal.dimension());
    }

    @Test
    public void testHamming() throws Exception {
        assertEquals("Wrong hamming for goal board",0,goal.hamming());
        assertEquals("Wrong hamming for board A",7,A.hamming());
        assertEquals("Wrong hamming for board B",1,B.hamming());
    }

    @Test
    public void testManhattan() throws Exception {
        assertEquals("Wrong manhattan for goal board",0,goal.manhattan());
        assertEquals("Wrong manhattan for board A",14,A.manhattan());
        assertEquals("Wrong manhattan for board B",1,B.manhattan());
    }

    @Test
    public void testIsGoal() throws Exception {
        assertTrue(goal.isGoal());
        assertFalse(A.isGoal());
        assertFalse(B.isGoal());
    }

    @Test
    public void testTwin() throws Exception {

    }

    @Test
    public void testEquals() throws Exception {
        assertFalse("Board report that it is equal to null.", A.equals(null));
        assertTrue("Board report that it is not equal to itself.", A.equals(A));
        assertFalse("Board report that it is equal to non equal board.",
                A.equals(goal));
        Board b = new Board(valuesBoardB);
        assertFalse("Board report that it is not equal to an equal Board (1).",
                b.equals(A));
        assertFalse("Board report that it is not equal to an equal Board (2).",
                A.equals(b));
    }

    @Test
    // it is not testing for corner-cases
    // Will not work correctly if equals() is not working
    public void testNeighbors() {
        int counter = 0;
        // Neighbors of A
        Iterator<Board> itA = A.neighbors().iterator();
        Board aLeft = new Board(valuesBoardALeftNeighbour);
        Board aRight = new Board(valuesBoardARightNeighbour);
        Board aUp = new Board(valuesBoardAUpNeighbour);
        Board aDown = new Board(valuesBoardADownNeighbour);
        // has left,right,up,down?

        boolean l = false, r = false, u = false, d = false;
        // ----------Test neighbors of A-------
        while (itA.hasNext()) {
            Board b = itA.next();

            // test for 'left'
            if (b.equals(aLeft)) {
                if (l == false) {
                    l = true;
                } else {
                    fail("Multiple 'left' neighbors.");
                }
            }

            // test for 'right'
            if (b.equals(aRight)) {
                if (r == false) {
                    r = true;
                } else {
                    fail("Multiple 'right' neighbors.");
                }
            }

            // test for 'up'
            if (b.equals(aUp)) {
                if (u == false) {
                    u = true;
                } else {
                    fail("Multiple 'up' neighbors.");
                }
            }

            // test for 'down'
            if (b.equals(aDown)) {
                if (d == false) {
                    d = true;
                } else {
                    fail("Multiple 'down' neighbors.");
                }
            }

            // count the number of neighbors
            counter++;

        }

        // check specific neighbors
        assertTrue("There should be a 'left' neighbor.", l);
        assertTrue("There should be a 'right' neighbor.", r);
        assertTrue("There should be an 'up' neighbor.", u);
        assertTrue("There should be a 'down' neighbor.", d);

        // Check the number of neighbors
        assertEquals("The number of neighbors is different than the actual number.",
                4,counter);


    }

    @Test
    public void testNeighboursCC() {
        // Test 1
        int[][] noUpRight = { { 1, 0 }, { 2, 3 } };
        int[][] nleft = { { 0, 1 }, { 2, 3 } };
        int[][] nDown = { { 1, 3 }, { 2, 0 } };
        // Test 2
        int[][] noDownLeft = { { 1, 2 }, { 0, 3 } };
        int[][] nUp = { { 0, 2 }, { 1, 3 } };
        int[][] nRight = { { 1, 2 }, { 3, 0 } };

        // Test 1
        Board b = new Board(noUpRight);
        Board bLeft = new Board(nleft);
        Board bDown = new Board(nDown);
        Iterator<Board> it = b.neighbors().iterator();

        boolean boolLeft = false, boolDown = false;
        int counter = 0;

        while (it.hasNext()) {
            Board x = it.next();
            counter++;

            if (x.equals(bLeft)) {
                if (!boolLeft) {
                    boolLeft = true;
                } else {
                    fail("Multiple \"left\" neighbors.");
                }
            }

            if (x.equals(bDown)) {
                if (!boolDown) {
                    boolDown = true;
                } else {
                    fail("Multiple \"down\" neighbors.");
                }
            }
        }
        assertTrue("There should be a 'down' neighbor.", boolDown == true);
        assertTrue("There should be a 'left' neighbor.", boolLeft == true);
        assertEquals("(Test 1)Wrong number of neighbors returned. ",
                2, counter);

        // Test 2
        b = new Board(noDownLeft);
        Board bRight = new Board(nRight);
        Board bUp = new Board(nUp);
        it = b.neighbors().iterator();

        boolean boolRight = false, boolUp = false;
        counter = 0;

        while (it.hasNext()) {
            Board x = it.next();
            counter++;

            if (x.equals(bRight)) {
                if (!boolRight) {
                    boolRight = true;
                } else {
                    fail("Multiple \"right\" neighbors.");
                }
            }

            if (x.equals(bUp)) {
                if (!boolUp) {
                    boolUp = true;
                } else {
                    fail("Multiple \"up\" neighbors.");
                }
            }
        }
        assertTrue("There should be a 'up' neighbor.", boolUp == true);
        assertTrue("There should be a 'right' neighbor.", boolRight == true);
        assertEquals("(Test 2)Wrong number of neighbors returned: ",
                2,counter);
    }

    @Test
    public void testTwim(){
        //TODO
    }

}
